//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Provenance
{
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ProvenanceInterfaces;
    using Microsoft.Research.Provenance.ProvenanceDataService;
    using Microsoft.Practices.Unity;
    using SR = Microsoft.Research.DataLayer;
    using Microsoft.Research.Provenance.Controls;
    using Microsoft.Research.DataLayer;
    using System.Windows.Controls;
    using System;
    using System.Windows;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using System.Collections.Generic;

    /// <summary>
    /// Interaction logic for ProvenanceControl.xaml
    /// </summary>
    public sealed partial class ProvenanceControl : UserControl, IProvenanceRegionManager, IDisposable
    {

        /// <summary>
        /// Unity Container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Module implementaion.
        /// </summary>
        private IProvenanceModule module;

        /// <summary>
        /// This is used to access connection manager API.
        /// </summary>
        private ConnectionManager connMgr;

        /// <summary>
        /// The registry connection.
        /// </summary>
        private SR.Connection registryConnection;

        /// <summary>
        /// The data retriever.
        /// </summary>
        private DataRetriever dataRetriever;

        /// <summary>
        /// The datatypes info dictionary. Contains the color codes and the names of the recognized datatypes.
        /// </summary>
        private Dictionary<String, DataTypeModel> dataTypes = new Dictionary<string, DataTypeModel>();

        /// <summary>
        /// Job Id  
        /// </summary>
        public Guid JobId { get; set; }

        /// <summary>
        /// Gets the job name associated.
        /// </summary>
        public string JobName
        {
            get
            {
                string jobName = string.Empty;

                if (this.dataRetriever != null && this.dataRetriever.CurrentJob != null)
                {
                    jobName = this.dataRetriever.CurrentJob.Name;
                }

                return jobName;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="jobId">Guid</param>
        /// <param name="dataTypes">The data types.</param>
        public ProvenanceControl(Guid jobId, Dictionary<String, DataTypeModel> dataTypes)
            : this(null, jobId, dataTypes)
        {
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="connection">The connection.</param>
        /// <param name="jobId">Guid</param>
        /// <param name="dataTypes">The data types.</param>
        public ProvenanceControl(SR.Connection connection, Guid jobId, Dictionary<String, DataTypeModel> dataTypes)
        {
            InitializeComponent();

            this.registryConnection = connection;
            this.JobId = jobId;
            this.dataTypes = dataTypes;

            this.InitializeProvenance();
        }

        /// <summary>
        /// This function is used to load teh provenance control.
        /// </summary>
        private void InitializeProvenance()
        {
            try
            {
                this.InitializeRegistry();
                this.GetRegistryConnection();
                this.InitializeContainer();
                this.InitializeDataRetriever();
                this.InitializeModules();
                this.container.Resolve<IProvenance>().Start(this.JobId);
            }
            catch (TridentCustomException)
            {
                throw;
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleAndThrowBusinessLogicException(
                    exception,
                    TridentErrorConstants.ErrorNumber1100000018,
                    ProvenanceResourceManager.GetString("Trident_LoaderError"));
            }
        }

        /// <summary>
        /// Initialize the container.
        /// </summary>
        private void InitializeContainer()
        {
            this.container = new UnityContainer();
            this.container.RegisterInstance<IUnityContainer>(this.container, new ExternallyControlledLifetimeManager());
            this.container.RegisterInstance<Dictionary<string, DataTypeModel>>("Datatypes", this.dataTypes, new ExternallyControlledLifetimeManager());
            this.container.RegisterInstance<IProvenanceRegionManager>(this, new ExternallyControlledLifetimeManager());
            this.container.RegisterInstance(typeof(SR.Connection), this.registryConnection, new ExternallyControlledLifetimeManager());
            this.container.RegisterInstance(typeof(Guid), this.JobId, new ExternallyControlledLifetimeManager());
        }

        /// <summary>
        /// Initialize the modules.
        /// </summary>
        private void InitializeModules()
        {
            try
            {
                this.module = this.container.Resolve<ProvenanceModule>();
                this.module.Initialize();
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }
        }

        /// <summary>
        /// Initialize the data retriever.
        /// </summary>
        private void InitializeDataRetriever()
        {
            try
            {
                ExternalRegistryHelperProxy.Initialize(this.registryConnection);

                this.dataRetriever = new DataRetriever(this.container, this.JobId, null, this.registryConnection);
                this.container.RegisterInstance<DataRetriever>(this.dataRetriever, new ExternallyControlledLifetimeManager());
            }
            catch (TridentCustomException)
            {
                throw;
            }
        }

        /// <summary>
        /// Initializes the registry.
        /// </summary>
        private void InitializeRegistry()
        {
            try
            {
                // Initialize Connection.
                SR_Connection.Init();
                connMgr = ConnectionManager.Create(ConnectionManager.CancelBehavior.ThrowException);
                connMgr.UIStyle = ConnectionManagerBase.UIStyleType.Lite;
            }
            catch (ConnectionFailure connectionFailure)
            {
                TridentErrorHandler.HandleUIException(connectionFailure);
                TridentMessageBox.ShowTridentErrorMessageBox(connectionFailure.InnerException.Message);
                Environment.Exit(0);
            }
            catch (InvalidOperationException exception)
            {
                TridentErrorHandler.HandleUIException(exception);
                TridentMessageBox.ShowTridentErrorMessageBox(exception.Message);
                Environment.Exit(0);
            }
            catch (Exception exception)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(exception);
                Environment.Exit(0);
            }
        }


        /// <summary>
        /// Get the registry connection.
        /// </summary>
        private void GetRegistryConnection()
        {
            bool isValid = false;

            if (this.registryConnection == null)
            {
                BackendStorageException connFailure;
                if (connMgr.HasDefaultConnection && !connMgr.TestConnection(connMgr.DefaultConnection, out connFailure))
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(connFailure.InnerException.Message);
                    isValid = false;
                }
                else
                {
                    isValid = true;
                }
            }

            do
            {
                try
                {
                    if (this.registryConnection == null)
                    {
                        if (!isValid)
                        {
                            registryConnection = connMgr.PickConnection(ConnectionManager.ConnectionUI.AlwaysShowUI);
                        }
                        else
                        {
                            registryConnection = connMgr.PickConnection(ConnectionManager.ConnectionUI.PromptIfNeeded);
                        }
                    }
                    else
                    {
                        // need to create clone fot existing registry connection.
                        this.registryConnection = this.registryConnection.Clone();
                        this.registryConnection.Open();
                    }

                    if (null != registryConnection)
                    {
                        if (!Helper.IsValidConnection(registryConnection))
                        {
                            TridentMessageBox.ShowTridentErrorMessageBox(
                                TridentCommonResourceManager.GetString("InvalidRegistryConnection"));
                            this.registryConnection = null;
                            isValid = false;
                            continue;
                        }

                        this.registryConnection.AutoSave = false;

                        TridentAuthentication authenticate = new TridentAuthentication(registryConnection);
                        authenticate.AuthenticateUser();
                    }

                    isValid = true;
                }
                catch (Microsoft.Research.DataLayer.ConnectionFailure connectionFailure)
                {
                    TridentErrorHandler.HandleUIException(connectionFailure);
                    TridentMessageBox.ShowTridentErrorMessageBox(connectionFailure.InnerException.Message);
                    isValid = false;
                }
                catch (InvalidOperationException exception)
                {
                    TridentErrorHandler.HandleUIException(exception);
                    TridentMessageBox.ShowTridentErrorMessageBox(exception.Message);
                    isValid = false;
                }
                catch (TridentCustomException customExp)
                {                    
                    TridentErrorHandler.HandleTridentCustomExceptionInUI(customExp);
                    isValid = false;
                }
                catch (Exception exception)
                {
                    TridentErrorHandler.HandleKnownExceptionsInUI(exception);
                    isValid = false;
                }
            } while (!isValid);

            if (null == registryConnection)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(
                    TridentCommonResourceManager.GetString("RegistryNotSelected"));
                Environment.Exit(0);
            }
        }

        #region IRegionManager Members

        public void Add(object view, string regionName)
        {
            Grid region = this.FindName(regionName) as Grid;
            if (region != null)
            {
                region.Children.Add(view as UIElement);
            }
        }

        #endregion


        #region IDisposable Members

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// nullify and Disposes the object 
        /// </summary>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.container.Dispose();
                this.container = null;
                this.registryConnection.Close();
                this.registryConnection = null;
                this.module = null;
                this.dataRetriever = null;
            }
        }
        #endregion
    }
}
